/*
*  This file is part of mangusta
*
*  mangusta is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  mangusta is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with mangusta; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  US
*
*  Author: <antonino.calderone@ericsson.com>, <acaldmail@gmail.com>
*
*/


/* -------------------------------------------------------------------------- */

#include "nu_lang_defs.h"
#include "nu_builtin_help.h"
#include "nu_about.h"


/* -------------------------------------------------------------------------- */

namespace nu
{


/* -------------------------------------------------------------------------- */

builtin_help_t* builtin_help_t::_instance = nullptr;


/* -------------------------------------------------------------------------- */

builtin_help_t& builtin_help_t::get_instance()
{
   if (_instance)
      return *_instance;

   _instance = new builtin_help_t();
   assert(_instance);

   builtin_help_t & hmap = *_instance;


   // Commands  //-----------------------------------------------------

   hmap[ {lang_item_t::COMMAND, "!"}] =
   {
      "Executes shell command\n"
      "See also Shell",
      "! shell_command\n"
   };

   hmap[ {lang_item_t::COMMAND, "?"}] =
   {
      "Executes Print instruction\n"
      "See also Print",
      "? [parameters...]"
   };

   hmap[ {lang_item_t::COMMAND, "run"}] =
   {
      "Executes the program currently in memory",
      "Run [linenumber]"
   };

   hmap[ {lang_item_t::COMMAND, "cont"}] =
   {
      "Continue interrupted program execution",
      "Cont"
   };

   hmap[ {lang_item_t::COMMAND, "resume"}] =
   {
      "Resume program execution from next line of interrupted one",
      "Resume"
   };

   hmap[ {lang_item_t::COMMAND, "break"}] =
   {
      "Set a breakpoint. If no parameters are specified,\n"
      "list lines with active breakpoints. Conditional breakpoint\n"
      "may be set using a boolean expression. Breakpoint is enabled\n"
      "if the expression becomes true during program execution",
      "Break [[linenum] [If expression]]"
   };

   hmap[ {lang_item_t::COMMAND, "rmbrk"}] =
   {
      "Remove a breakpoint",
      "RmBrk linenum"
   };

   hmap[ {lang_item_t::COMMAND, "clrbrk"}] =
   {
      "Remove all active breakpoints",
      "ClrBrk"
   };

   hmap[ {lang_item_t::COMMAND, "load"}] =
   {
      "Loads a program file in memory",
      "Load filename\n"
   };

   hmap[ {lang_item_t::COMMAND, "exec"}] =
   {
      "Loads and runs a program file",
      "Exec filename\n"
   };

   hmap[ {lang_item_t::COMMAND, "save"}] =
   {
      "Saves current program to file",
      "Save filename"
   };

   hmap[ {lang_item_t::COMMAND, "pwd"}] =
   {
      "Retrieves the current working directory",
      "Pwd"
   };

   hmap[ {lang_item_t::COMMAND, "cd"}] =
   {
      "Changes the current working directory",
      "Cd new_working_dir"
   };

   hmap[ {lang_item_t::COMMAND, "list"}] =
   {
      "Lists all or part of a program",
      "List [hn] [<line_number>][-<line_number>]\n"
      "List [hn] [<line_number>-]"
   };

   hmap[ {lang_item_t::COMMAND, "clr"}] =
   {
      "Clears all variables",
      "Clr"
   };

   hmap[ {lang_item_t::COMMAND, "grep"}] =
   {
      "Lists lines containing a match to the given pattern",
      "Grep pattern"
   };

   hmap[ {lang_item_t::COMMAND, "new"}] =
   {
      "Deletes the program currently "
      "in memory and clear all variables",
      "New"
   };

   hmap[ {lang_item_t::COMMAND, "format"}] =
   {
      "Formats source code and renumbers program lines",
      "format [increment(default=10)]"
   };

   hmap[ {lang_item_t::COMMAND, "tron"}] =
   {
      "Enables program tracing",
      "TrOn"
   };

   hmap[ {lang_item_t::COMMAND, "troff"}] =
   {
      "Disables program tracing",
      "TrOff"
   };

   hmap[ {lang_item_t::COMMAND, "exit"}] =
   {
      std::string("Exits the ") + about::progname,
      "Exit"
   };

   hmap[ {lang_item_t::COMMAND, "ver"}] =
   {
      std::string("Prints out information about ") +
      about::progname + "version",
      "Ver"
   };


   hmap[ {lang_item_t::COMMAND, "ctx"}] =
   {
      "Show run-time data",
      "Vars"
   };


   hmap[ {lang_item_t::COMMAND, "meta"}] =
   {
      "Show build-up metadata",
      "Meta"
   };


   hmap[ {lang_item_t::COMMAND, "help"}] =
   {
      "Provides a description about keyword. In case keyword is not "
      "specified, shows the list of keywords, including commands, "
      "instructions, functions and operators.",
      "Help [keyword]"
   };


   hmap[ {lang_item_t::COMMAND, "apropos"}] =
   {
      "Searches a help content containing short descriptions of commands "
      "for keywords and displays the result on the standard output.",
      "Apropos keyword"
   };

   //Multiplies two numbers
   //Performs a logical exclusion on two Boolean expressions

   hmap[ {lang_item_t::OPERATOR, "xor"}] =
   {
      "Performs a logical exclusion on two Boolean expressions",
      "x Xor y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_AND}] =
   {
      "Performs a logical conjunction on two Boolean expressions",
      "x " PROGLANG_OP_AND " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_OR}] =
   {
      "Performs a logical conjunction on two Boolean expressions",
      "x " PROGLANG_OP_OR " y"
   };

   hmap[ {lang_item_t::OPERATOR, "bxor"}] =
   {
      "Returns a one in each bit position for which the corresponding\n"
      "bits of either but not both operands are ones",
      "x bXor y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_BAND}] =
   {
      "Returns a one in each bit position for which the corresponding\n"
      "bits of both operands are ones",
      "x " PROGLANG_OP_BAND " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_BOR}] =
   {
      "Returns a one in each bit position for which the corresponding\n"
      "bits of either or both operands are ones",
      "x " PROGLANG_OP_BOR " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_BSHL}] =
   {
      "Shifts x in binary representation y bits to the left,\n"
      "shifting in zeros from the right",
      "x " PROGLANG_OP_BSHL " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_BSHR}] =
   {
      "Shifts x in binary representation y bits to the right,\n"
      "discarding bits shifted off",
      "x " PROGLANG_OP_BSHR " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_POWER}] =
   {
      "Raises a number to the power of another number",
      "x " PROGLANG_OP_POWER " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_DIV}] =
   {
      "Divides two numbers and returns a floating-point result",
      "x " PROGLANG_OP_DIV " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_MUL}] =
   {
      "Multiplies two numbers",
      "x " PROGLANG_OP_MUL " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_MINUS}] =
   {
      "Returns the difference between two numeric expressions "
      "or the negative value of a numeric expression",
      "[x] " PROGLANG_OP_MINUS " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_PLUS}] =
   {
      "Adds two numbers or returns the positive value of a "
      "numeric expression.\nCan also be used to concatenate two "
      "string expressions",
      "[x] " PROGLANG_OP_PLUS " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_MOD}] =
   {
      "Divides two numbers and returns only the remainder",
      "x " PROGLANG_OP_MOD " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_NOTEQUALS}] =
   {
      "'Not equal to' operator",
      "x " PROGLANG_OP_NOTEQUALS " y"
   };

   hmap[ {lang_item_t::OPERATOR, "="}] =
   {
      "'Assign operator",
      "x = y"
   };


   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_EQUALS}] =
   {
      "'Equal to' operator ",
      "x " PROGLANG_OP_EQUALS " y"
   };


   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_LT}] =
   {
      "'Less than' operator",
      "x " PROGLANG_OP_LT " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_GT}] =
   {
      "'Greater than' operator",
      "x " PROGLANG_OP_GT " y"
   };


   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_LTEQ}] =
   {
      "'Less than or equal to' operator",
      "x " PROGLANG_OP_LTEQ " y"
   };

   hmap[ {lang_item_t::OPERATOR, PROGLANG_OP_GTEQ}] =
   {
      "'Greater than or equal to' operator",
      "x " PROGLANG_OP_GTEQ " y"
   };


   // Instructions //--------------------------------------------------

   //CHDIR expression
   hmap[ {lang_item_t::INSTRUCTION, "chdir"}] =
   {
      "Changes the current working directory",
      "ChDir dir_string"
   };

   //If expression Then statement(s) [Else statement(s)]
   hmap[ {lang_item_t::INSTRUCTION, "if"}] =
   {
      "Makes a decision regarding program flow based on "
      "the result returned by an expression\n"
      "If the result of expression is nonzero(logical true),"
      " the Then statement(s) is executed.\n"
      "If the result of expression is zero (false), the Then statement(s)"
      " is ignored and the Else statement(s), if present, is executed.\n"
      "Otherwise, execution continues with the next executable statement",
      "If expression Then statement(s) [Else statement(s)]"
   };

   hmap[ {lang_item_t::INSTRUCTION, "then"}] =
      hmap[ {lang_item_t::INSTRUCTION, "if"}];

   hmap[ {lang_item_t::INSTRUCTION, "else"}] =
      hmap[ {lang_item_t::INSTRUCTION, "if"}];

   ///for (...) //TODO
   hmap[ {lang_item_t::INSTRUCTION, PROGLANG_STMT_FOR}] =
   {
      //TODO
      "Executes a series of instructions a specified number "
      "of times in a loop.\n"
      "- variable is used as a counter\n"
      "- x,y, and z are numeric expressions\n"
      "- Step z specifies the counter increment for each loop."
      "If Step is not specified, the increment is assumed to be 1\n"
      "The first numeric expression (x) is the initial value of the counter.\n"
      "The second numeric expression (y) is the final value of the counter.\n"
      "Program lines following the For statement are executed until the Next"
      " statement is encountered. Then, the counter is incremented by the amount "
      "specified by Step",
      "  For variable=x TO y [Step z] [statement(s) [Next [variable]]]\n\n"
      "  For variable=x TO y [Step z] [statement(s)]\n"
      "  ... [statement(s)]\n"
      "      [Exit For]\n"
      "  ... [statement(s)]\n"
      "  Next [variables]\n"
   };

   //while ... end while
   hmap[ {lang_item_t::INSTRUCTION, "while"}] =
   {
      "Executes a series of statements as long as a "
      "given condition is not zero (True)\n"
      "while...end while loops can be nested to any level.\n"
      "Each 'end while' matches the most recent 'while'.\n"
      "The while statement syntax has these parts:\n"
      "- condition  :	 Numeric expression or string expression "
      "that evaluates to True or False.\n"
      "- statements : Optional. One or more statements executed while "
      "condition is True.\n"
      "- exit while : Optional. Transfers control out of the while-block.\n",
      "  while condition \n"
      "      [statement(s)]  \n"
      "      [exit while]    \n"
      "      [statement(s)]  \n"
      "  end while "
   };

   //DO ... LOOP While cond
   hmap[ {lang_item_t::INSTRUCTION, PROGLANG_STMT_DO}] =
   {
      "Executes a series of statements as long as a "
      "given condition is not zero (True)\n"
      "do...loop loops can be nested to any level.\n"
      "Each 'loop while' matches the most recent Do.\n"
      "The Do...Loop While statement syntax has these parts:\n"
      "- condition  :	 Numeric expression or string expression "
      "that evaluates to True or False.\n"
      "- statements : Optional. One or more statements executed while "
      "condition is True.\n"
      "- exit do : Optional. Transfers control out of the 'do-loop-while' block.\n",
      "  do\n"
      "      [statement(s)]  \n"
      "      [exit do]       \n"
      "      [statement(s)]  \n"
      "  loop while condition"
   };

   //Exit For [variable]
   hmap[ {lang_item_t::INSTRUCTION, "exit_for"}] =
   {
      "Causes program execution to jump to the first statement"
      " following the next [variable] statement. ",
      " exit for "
   };

   //Exit While
   hmap[ {lang_item_t::INSTRUCTION, "exit_while"}] =
   {
      "Causes program execution to jump to the first statement"
      " following the Wend statement.",
      "Exit While"
   };

   hmap[ {lang_item_t::INSTRUCTION, "goto"}] =
   {
      "Branches unconditionally out of the normal program sequence "
      "to a specified line number or label",
      "GoTo line-number | label"
   };

   hmap[ {lang_item_t::INSTRUCTION, "locate"}] =
   {
      "Moves the cursor to the specified position on the active screen",
      "Locate row,col"
   };

   hmap[ {lang_item_t::INSTRUCTION, "cout"}] =
   {
      "Prints list of expressions to standard output\n"
      "If list of expressions is included, the values of the expressions "
      "are displayed.\nExpressions in the list may be numeric and/or string "
      "expressions, separated by comma.\n"
      "Literal strings in the list must be enclosed in double quotation marks",
      "cout [list of expressions]"
   };


   hmap[ {lang_item_t::INSTRUCTION, "read"}] =
   {
      "Reads data from file that will be placed into a variable.\n"
      "Read statement overwrites the destination variable content with data content.\n"
      "If the variable does not exist, it will be created\n"
      "Using a byte vector variable or a string the size may be \n"
      "any positive integer value.\n"
      "Using plain type variable like integer or float the size \n"
      "must be equal to this variable size\n",
      "Read filenum, variable, size\n"
   };

   hmap[ {lang_item_t::INSTRUCTION, "array"}] =
   {
      "Allocate storage space for array variable\n"
      "An array must be declared in order to be used.\n"
      "The array statement sets all the elements of the specified "
      "arrays to an initial value of zero or empty string\n",
      "array type1 var1(dim1), type2 var2(dim2), ..., typeN varN(dimN)\n"
   };

   hmap[ {lang_item_t::INSTRUCTION, PROGLANG_STMT_FUNCTION}] =
   {
      "Define a a block of code that performs a single task\n"
      "The block is enclosed by a declaration statement.\n"
      "The procedure defines zero or more parameters, each of which\n"
      "represents a value it expects you to pass to it.\n"
      "A function returns a value. This value is assigned to its\n"
      "own function name variable in one or more statements "
      "of the procedure.\n"
      "See also: sub",
      PROGLANG_STMT_FUNCTION" ret_type function_name ( [type1 p1, type2 p2, ...] ) {\n"
      "   [statement(s)]\n"
      "   [function_name = return-value\n"
      "   [exit func]]\n"
      "   [statement(s)]\n"
      "   function_name = return-value\n"
      "}\n"
   };

   hmap[ {lang_item_t::INSTRUCTION, "const"}] =
   {
      "Assigns the value of an expression to a constant variable.\n",
      "const type variable = expression\n"
   };

   return *_instance;
}


/* -------------------------------------------------------------------------- */

std::string builtin_help_t::help(const std::string& s_item)
{
   std::string result;

   if (s_item.empty())
   {
      lang_item_t last_sel = lang_item_t::MODULE;

      std::lock_guard<std::recursive_mutex> _lock(_mtx);

      for (const auto & h : *this)
      {
         if (last_sel != h.first.first)
         {
            last_sel = h.first.first;

            switch (h.first.first)
            {
               case lang_item_t::COMMAND:
                  result += "\nCOMMANDs\n";
                  break;

               case lang_item_t::FUNCTION:
                  result += "\n\nFUNCTIONs\n";
                  break;

               case lang_item_t::INSTRUCTION:
                  result += "\n\nINSTRUCTIONs\n";
                  break;

               case lang_item_t::OPERATOR:
                  result += "\n\nOPERATORs\n";
                  break;

               case lang_item_t::MODULE:
                  result += "\n\nMODULEs\n";
                  break;
            }
         }

         std::string keyword = h.first.second;
         enum
         {
            KEYWORDFORMATLEN = 24, MAXDESCLEN = 48
         };

         while (keyword.size() < KEYWORDFORMATLEN) keyword += " ";

         std::string desc = h.second.first;
         int lfpoint = int(desc.find('\n'));
         std::replace(desc.begin(), desc.end(), '\n', ' ');
         bool ellipses =
            lfpoint > MAXDESCLEN ||
            (lfpoint < 0 && desc.size()>MAXDESCLEN);

         if (lfpoint > MAXDESCLEN || lfpoint < 0)
         {
            lfpoint = MAXDESCLEN;
         }

         result += keyword + desc.substr(0, lfpoint + 1);
         result += ellipses ? "...\n" : "\n";
      }
   }

   else
   {
      std::lock_guard<std::recursive_mutex> _lock(_mtx);

      for (int it = 0;
            it <= static_cast<int>(lang_item_t::MODULE);
            ++it)
      {
         help_item_t item({ static_cast<lang_item_t>(it), s_item });

         auto i = this->find(item);

         if (i != this->end())
         {
            result += "\n" + (*this)[item].second;
            result += "\n\n" + (*this)[item].first + "\n";

         }
      }
   }

   return result;
}


/* -------------------------------------------------------------------------- */

std::string builtin_help_t::apropos(const std::string& s_item)
{
   std::string result;

   if (!s_item.empty())
   {
      lang_item_t last_sel = lang_item_t::MODULE;

      std::lock_guard<std::recursive_mutex> _lock(_mtx);

      for (const auto & h : *this)
      {
         if (last_sel != h.first.first)
         {
            last_sel = h.first.first;
         }

         std::string keyword = h.first.second;
         std::string desc = h.second.first;

         std::string k = keyword;
         std::string d = desc;

         size_t found_k = k.find(s_item);
         size_t found_d = d.find(s_item);

         if (found_k != std::string::npos || found_d != std::string::npos)
         {
            enum
            {
               KEYWORDFORMATLEN = 24, MAXDESCLEN = 48
            };

            while (keyword.size() < KEYWORDFORMATLEN) keyword += " ";

            int lfpoint = int(desc.find('\n'));
            std::replace(desc.begin(), desc.end(), '\n', ' ');
            bool ellipses =
               lfpoint > MAXDESCLEN ||
               (lfpoint < 0 && desc.size()>MAXDESCLEN);

            if (lfpoint > MAXDESCLEN || lfpoint < 0)
            {
               lfpoint = MAXDESCLEN;
            }

            result += keyword + desc.substr(0, lfpoint + 1);
            result += ellipses ? "...\n" : "\n";
         }
      }
   }

   return result;
}


/* -------------------------------------------------------------------------- */

} // namespace nu


/* -------------------------------------------------------------------------- */
